/* advrp.c -- holds main and related functions for advrp
 *
 *    Copyright 2010 Patrick Allen
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
*/

#include <inttypes.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <pwd.h>
#include <getopt.h>
#include <sys/time.h>
#include <time.h>

#include "advrp.h"
#include "receiver.h"
#include "debug.h"
#include "init.h"
#include "holddown.h"
#include "sender.h"
#include "verify.h"
#include "hash.h"

#define OPT_LIST "n:u:v:l:i:d:D:fhp:"

static struct option LONGOPT_LIST[] = {
	{ "network", 1, 0, 'n' },
	{ "update-timer", 1, 0, 'u' },
	{ "invalid-timer", 1, 0, 'v' },
	{ "interface", 1, 0, 'i' },
	{ "debug", 1, 0, 'd' },
	{ "debug-flags", 1, 0, 'D' },
	{ "background", 0, 0, 'b' },
	{ "holddown-timer", 1, 0, 'l' },
	{ "user", 1, 0, 'p' },
	{ "help", 0, 0, 'h' },
	{ 0, 0, 0, 0 }
};

#define USAGE_MESSAGE \
	"Usage:  advrp -i <INTERFACE> [OPTIONS]\n"\
	"\n"\
	"OPTIONS:  -n --network         Network to advertise (CIDR notation)\n"\
	"          -u --update-timer    Update timer in sec\n"\
	"          -v --invalid-timer   Invalid timer in sec\n"\
	"          -l --holddown-timer  Holddown timer in sec\n"\
	"          -i --interface       Interface to advertise on\n"\
	"          -d --debug           Debug level\n"\
	"          -D --debug-flags     Timestamps (1), function names (2)\n"\
	"          -b --background      Run in background\n"\
	"          -p --user            Drop privs to this user\n"\
	"          -h --help            Display this usage message\n"\
	"\n"\
	"Example: advrp -i eth0 -i eth1 -n 192.168.1.0/24 -n 192.168.2.0/24\n"\
	"\n"\
	"At a minimum, one interface must be given to listen on.\n"

struct id_set {
        uid_t uid;
        gid_t gid;
} dropid = { 0, 0 };

pthread_t threadID[THREAD_COUNT];
pthread_mutex_t mutexInterface, mutexNeighbor;     // not used
pthread_mutex_t mutexHaveNeighbor;
pthread_mutex_t mutexAdvrp;
pthread_mutex_t mutexCreate;
pthread_cond_t condCreate;
pthread_attr_t attrAdvrp;
pthread_cond_t condSleep;
pthread_mutex_t mutexSleep;
internalRoute * firstInternalRoute, * lastInternalRoute;
neighbor * lastNeighbor = NULL;
neighbor * firstNeighbor = NULL;
neighbor * firstRemoteNeighbor = NULL;
neighbor * self = NULL;
uint32_t INVALID_TIMER;
uint32_t UPDATE_TIMER;
uint32_t HOLDDOWN_TIMER;
double START_TIME = 0;

extern interface * firstInterface;
extern hdNetwork * firstHdNetwork;
extern uint64_t DBG_LVL;
extern uint64_t DBG_FLAGS;
extern _Bool haveNeighbor;
extern pthread_cond_t condNeighbor;

uint32_t set_timers(char * reqUpdateT, char * reqInvalidT, char * reqHolddownT);
interface * find_if_by_name(char * ifname);
interface * request_interface(char * optarg, interface ** firstIf);
int set_debug(char * optarg);
int daemonize(void);
int use_interface(interface * reqif);
void cleanup(void);
int is_number(char * s);
void usage(void);
void router(int cmd);
void sig_handler(int sig);
int drop_privs(int regain);
int gain_privs(void);
int check_user(char * user, struct id_set * ids);
int request_network(char * prefix, updateRecord * reqNet, int * netCount);
int add_local_prefixes(updateRecord * reqNet, int netCount);
uint64_t dbg_name_lookup(char * name);
in_addr_t make_snmask(uint8_t preflen);

int main(int argc, char * argv[])
{
	interface * firstReqIf = NULL;          // User requested interfaces
	interface * reqif, * nextif;
	int netCount = 0;                       // # of requested networks
	updateRecord reqNet[RECORDS_IN_UPDATE]; // User requested networks
	char * reqDbgLvl = NULL, * reqDbgFlags = NULL;  // Requested debug info
	char * reqUpdateT = NULL, * reqInvalidT = NULL, * reqHolddownT = NULL;
	int i, optind;
	int background = 0;
	char c;
	char * s;
	long maxname;   /* Max username size */
	sigset_t newmask;
	struct timeval tv;
	struct sigaction saction;

	/* Set start time */
	gettimeofday(&tv, NULL);
	START_TIME = tv.tv_sec + (tv.tv_usec / 1000000.0);

	/* Catch signals 1, 2, and 15 in this thread */
	newmask.__val[0] = SIGINT;
	newmask.__val[1] = SIGTERM;
	newmask.__val[2] = SIGHUP;
	pthread_sigmask(SIG_UNBLOCK, &newmask, NULL);
	saction.sa_handler = sig_handler;
	saction.sa_mask = newmask;
	sigaction(SIGINT, &saction, NULL);
	sigaction(SIGTERM, &saction, NULL);
	sigaction(SIGHUP, &saction, NULL);

	if (geteuid() != 0) {
		DBG(ERR, "%s must be ran as root.", 
		    (char *) basename(argv[0]));
		return 1;
	}

	if (pidfile_create())
		return 1;

	/* Parse input */
	while ((c = getopt_long(argc, argv, OPT_LIST, LONGOPT_LIST, &optind)) 
	                                                               != -1) {
		switch (c) {
			case 'n':
				if (request_network(optarg, reqNet, 
				                      &netCount)) {
					DBG(ERR, "Bad -n argument.");
					usage();
				}
				break;
			case 'u':
				reqUpdateT = optarg;
				break;
			case 'v':
				reqInvalidT = optarg;
				break;
			case 'i':
				if (! request_interface(optarg, &firstReqIf)) {
					DBG(ERR, "Bad -i argument.");
					usage();
				}
				break;
			case 'd':
				reqDbgLvl = optarg;
				break;
			case 'D':
				reqDbgFlags = optarg;
				break;
			case 'b':
				if (! background)
					background++;
				break;
			case 'l':
				reqHolddownT = optarg;
				break;
			case 'p':
				if (! check_user(optarg, &dropid))
					usage();
				break;
			case 'h':
				usage();
				break;
			default:
				DBG(ERR, "Unrecognized option %c.", optopt);
				usage();
				break;
		}
	}

	/* Set debugging flags */
	if (set_dbgflags(reqDbgFlags)) {
		usage();
	}

	/* Set debugging level */
	if (set_debug(reqDbgLvl)) {
		usage();
	}

	/* Get a list of usable interfaces */
	if (init_interfaces() < 1) {
		DBG(ERR, "No usable interfaces were detected. Exiting.");
		cleanup();
	}

	/* Check if the required -i argument was supplied and is valid */
	if (! firstReqIf) {
		DBG(ERR, "-i argument is required, but was not found.");
		usage();
	}

	/* Activate requested interfaces */
	DBG(DEV_1, "Checking for valid -i arguments.");
	for (reqif = firstReqIf; reqif != NULL; reqif = nextif) {
		if (use_interface(reqif)) {
			DBG(ERR, "Interface %s not found. It must be up and "
			         "have an IP address.", reqif->ifname);
			cleanup();
		}

		/* Free this requested interface */
		nextif = reqif->next;
		free(reqif);
	}

	/* Create self */
	firstNeighbor = init_self();
	
	/* Add the requested local networks */
	DBG(DEV_1, "Adding local routes.");
	add_local_prefixes(reqNet, netCount);

	/* Check for valid update and invalid timers */
	if (set_timers(reqUpdateT, reqInvalidT, reqHolddownT)) {
		cleanup();
	}

	/* Daemonize if asked */
	if (background) {
		if (daemonize()) {
			DBG(ERR, "Could not daemonize.");
		} else {
			DBG(DEV_1, "Daemonized successfully.");
		}
	} else {
		DBG(MAIN_1, "Running in foreground.");
	}

	/* Turn routing on */
	router(ON);

	/* Set the "detached" attribute for threads, so their memory is released
	 * upon exit without calling pthread_join. */
	pthread_attr_init(&attrAdvrp);
	pthread_attr_setdetachstate(&attrAdvrp, PTHREAD_CREATE_DETACHED);

	/* Set up threads, then wait to exit. After creating each thread, wait
	 * to be signalled before continuing. This prevents an odd behavior
	 * with the sender thread sometimes sending several updates at
	 * once when it first starts; it "seemed" to be ignoring or breaking
	 * out of its sleep() statements.
	 */
	pthread_mutex_lock(&mutexCreate);
	pthread_create(&threadID[0], &attrAdvrp, verify_loop, NULL);
	pthread_cond_wait(&condCreate, &mutexCreate);

	pthread_create(&threadID[1], &attrAdvrp, holddown_loop, NULL);
	pthread_cond_wait(&condCreate, &mutexCreate);

	pthread_create(&threadID[2], &attrAdvrp, sender_loop, NULL);
	pthread_cond_wait(&condCreate, &mutexCreate);

	pthread_create(&threadID[3], &attrAdvrp, receiver_loop, NULL);
	pthread_mutex_unlock(&mutexCreate);

	DBG(DEV_1, "Main thread id: %u", pthread_self());

	DBG(DEV_1, "Finished creating threads.");

	{
		uid_t ruid, euid, suid;
		getresuid(&ruid, &euid, &suid);
		DBG(DEV_1, "EUID is %u, RUID is %u, SUID is %u", euid, ruid,
		    suid);
	}

	/* Sleep rather than calling pthread_exit() so we can catch signals */
	for (;;)
		sleep(100000);
}

/* sig_handler
 * Ignore further signals, then clean up */
void sig_handler(int sig)
{
	uid_t ruid, euid, suid;

	signal(SIGINT, SIG_IGN);
	signal(SIGTERM, SIG_IGN);
	signal(SIGHUP, SIG_IGN);

	DBG(DEV_1, "sig_handler called from thread %u", pthread_self());
	getresuid(&ruid, &euid, &suid);
	DBG(DEV_1, "EUID is %u, RUID is %u, SUID is %u", euid, ruid, suid);

	DBG(ERR, "Signal %d received. Cleaning up.", sig);
	cleanup();
}

/* drop_privs
 * Drop effective privileges to the user specified in the dropid global var.
 * If dropid is set to 0, use the default "advrp" user instead.
 * If the user advrp does not exist, create it.
 */
int drop_privs(int regain)
{
	extern struct id_set dropid;
	struct passwd * pw;

	/* If no user is specified or it was set to root, use the default */
	if (dropid.uid == 0) {
		errno = 0;

		/* Return if user "advrp" does not exist */
		if (! (pw = getpwnam("advrp"))) {
			DBG(ERR, "Problem looking up default user advrp "
			    "(does user advrp exist?)");
			return 1;
		}

		dropid.uid = pw->pw_uid;
		dropid.gid = pw->pw_gid;
	}

	if (regain) {
		/* Drop only effective privs if we want to regain root later
	 	* (used to insert kernel routes and cleanup) */
		DBG(MAIN_1, "Temporarily dropping privs to uid %u, gid %u.",
		     dropid.uid, dropid.gid);
		setegid(dropid.gid);
		seteuid(dropid.uid);
	} else {
		/* Otherwise, drop all privs */
		DBG(MAIN_1, "Permanently dropping privs to uid %u, gid %u.",
		     dropid.uid, dropid.gid);
		setresuid(dropid.uid, dropid.uid, dropid.uid);
		setresgid(dropid.gid, dropid.gid, dropid.gid);
	}

	return 0;
}

/* gain_privs
 * Attempt to regain effective root privileges after they have been dropped.
 * Real or set-uid privs must still be zero.
 * Returns 0 if successful or 1 if root privs could not be regained.
 */
int gain_privs(void)
{
	errno = 0;

	/* Regain root privs */
	if (seteuid(0) == -1) {
		DBG(ERR, "Could not regain root privileges: %s",
		    strerror(errno));
		return 1;
	}

	return 0;
}

/* check_user()
 * Is the given username valid on this system?
 * Return 1 if found, otherwise return 0. Requested user may not be root.
 * Populate the given id_set structure with the uid and gid of the username if
 * it exists.
 */
int check_user(char * user, struct id_set * ids)
{
	struct passwd * pw;

	/* Was the string longer than the maximum login name? */
	if (strlen(user) >= sysconf(_SC_LOGIN_NAME_MAX)) {
		DBG(ERR, "Username was too long.");
		return 0;
	}

	/* Make sure the user exists */
	pw = getpwnam(user);
	if (! pw) {
		DBG(ERR, "Could not find user %s.", user);
		return 0;
	}

	if (pw->pw_uid == 0) {
		DBG(ERR, "Cannot \"drop\" privileges to root.");
		return 0;
	}

	ids->uid = pw->pw_uid;
	ids->gid = pw->pw_gid;

	return 1;
}

/* router()
 * Enable IP forwarding in the kernel and add a rule to use ADVRP's
 * routing table with a priority of 27.
 * For now, just using the system() function.
 * XXX Update me to use the table defined in advrp.h and not depend on 'echo'
 * or the ip utility.
 */
void router(int cmd)
{
	switch (cmd) {
		case ON:
			system("echo 1 > /proc/sys/net/ipv4/ip_forward");
			system("ip rule add from all lookup 27 prio 27");
			break;
		case OFF:
			system("echo 0 > /proc/sys/net/ipv4/ip_forward");
			system("ip rule del priority 27 2> /dev/null");
			break;
		default:
			break;
	}
	return;
}

int daemonize(void)
{
	pid_t pid;

	if ((pid = fork()) != 0)
		exit(0);

	/* setsid always throws an error */
	if (setsid() == -1)
		DBG(DEV_1, "setsid reported an error.\n");

	if ((pid = fork()) != 0)
		exit(0);

	/* XXX Should also take the precaution to change the working directory
	 * and set the umask... */
	return 0;
}

/* use_interface: Find this interface name and enable ADVRP on it if possible.
 * Return 0 if it can be used, otherwise return 1. */
int use_interface(interface * reqif)
{
	interface * cif;

	if (cif = find_if_by_name(reqif->ifname)) {
		cif->active = 1;
		return 0;
	} else {
		return 1;
	}
}

/* find_if_by_name: See if an interface was initialized with the given interface
 * name. Return the interface structure if it is found, otherwise return NULL.
 */
interface * find_if_by_name(char * ifname)
{
	interface * cif;

	for (cif = firstInterface; cif != NULL; cif = cif->next)
		if (! strcmp(cif->ifname, ifname))
			return cif;

	/* Interface not found */
	return NULL;
}

/* set_dbgflags
 * Set whether or not we should print timestamps, function names, etc., in
 * debug output. Input may be either decimal or hex. This is determined by
 * the presence of "0x" in front of the value you wish to set.
 * Return 1 if there is a problem, otherwise return 0.
 */
int set_dbgflags(char * flagstr)
{
	int base;
	uint64_t conv = 0;    /* Temp store for the converted value */
	char * start;         /* The start of the string we want to convert */
	char * endptr;

	if (! flagstr) {
		/* If no debug flags were given, just use timestamps */
		conv = TS_F;
	} else {
		if ((base = snip_get_base(&flagstr)) == -1) {
			return 1;
		}

		if (base == 0) {
			DBG(ERR, "Debug flags do not have names. Did you mean "
			    "-d?");
			return 1;
		}

		/* Perform the conversion and do error checking */
		endptr = &flagstr[strlen(flagstr) - 1];
		errno = 0;
		conv = strtoull(flagstr, &endptr, base);
		if (errno) {
			DBG(ERR, "Invalid debug flag setting: %s.",
			    strerror(errno));
			DBG(ERR, "Debug flag argument must be decimal or hex. "
			    "Hex arguments should begin with 0x or 0X.");
			return 1;
		}
	}

	DBG_FLAGS = conv;
	DBG(DEV_1, "Debug flags are set to 0x%.16llX", DBG_FLAGS);
	return 0;
}

/* snip_get_base
 * Does up to two of the following:
 * 0) If the argument contains letters, return 0
 * 1) If it's a number, get the base (either hex or dec)
 * 2) If it's hex, modify the argument so no hex prefix exists (the snip part)
 * Return the base, or -1 if there is a problem. Return 0 if there are letters.
 */
int snip_get_base(char ** s)
{
	char * c;
	int base;

	/* No negative numbers */
	if (*s[0] == '-') {
		DBG(ERR, "Negative debug level is not possible.");
		return -1;
	}

	if (strlen(*s) < 3) {

		/* If string length is 1 or 2, assume it is decimal */
		base = 10;
	} else if (((*s)[0] == '0') &&
	           ((*s)[1] == 'x' || (*s)[1] == 'X')) {

		/* Saw a hex prefix, so convert from hex. */
		base = 16;

		/* Cut the prefix off. */
		*s = &((*s)[2]);
	} else {

		/* No hex prefix; check if it contains any letters */ 
		for (c = *s; c[0] != '\0'; c++)
			if (isalpha(c[0]))
				return 0;

		/* It wasn't hex and it's not a name; assume it is decimal */
		base = 10;
	}

	return base;
}

/* set_debug
 * Verify that the given debug value is acceptable.
 * If it is, set the global DBG_LVL variable.
 * Return 1 on error, otherwise return 0.
 */
int set_debug(char * lvlstr)
{
	int base;
	char * endptr;
	uint64_t conv = 0;

	/* If no debug level is specified, just print errors. */
	if (! lvlstr) {
		DBG(DEV_1, "Setting debug level to default of 0x%.16llX", ERR);
		DBG_LVL = ERR;
		return 0;
	}

	/* Get the base, or determine if it is a name */
	if ((base = snip_get_base(&lvlstr)) == -1) {
		DBG(ERR, "Invalid debug level.");
		DBG(ERR, "Debug level should be one of: a decimal number, hex "
		    "number (with 0x prefix), or one or more valid names "
		    "separated by commas ONLY -- no spaces (SEND,RECV,MAIN,HOLD"
		    ",VRFY,KERN,DEV,ALL)");
		return 1;
	} else if (base == 0) {
		/* Is it a recognized name/list of names? */
		if (get_dbglvl_by_name(lvlstr, &conv)) {
			DBG(ERR, "Invalid debug level.");
			DBG(ERR, "Debug level should be one of: a decimal "
			    "number, hex number (with 0x prefix), or one or "
			    "more valid names separated by commas ONLY -- no "
			    "spaces (SEND,RECV,MAIN,HOLD,VRFY,KERN,DEV,ALL)");
			return 1;
		}
	} else {
		/* Decimal or hex -- do conversion and error checking */
		endptr = &lvlstr[strlen(lvlstr) - 1];
		errno = 0;
		conv = strtoull(lvlstr, &endptr, base);
		if (errno) {
			DBG(ERR, "Invalid debug level.")
			return 1;
		}
	}

	/* If we made it here, there is a good debug level in conv */

	/* Always print errors */
	conv |= ERR;

	/* Set the debug level */
	DBG(DEV_1, "Setting debug level to 0x%.16llX", conv);
	DBG_LVL = conv;
	return 0;
}

/* get_dbglvl_by_name
 * Convert debug level name(s) into an integer and place the result in conv.
 * Return 1 if there is a problem, otherwise return 0.
 */
int get_dbglvl_by_name(char * lvlstr, uint64_t * conv)
{
	char lvlcpy[strlen(lvlstr) + 1];      // copy of lvlstr
	char * saveptr;
	char * cptr;
	uint64_t ret;     // tmp store for return values from dbg_name_lookup()

	strncpy(lvlcpy, lvlstr, sizeof(lvlcpy));

	/* Break lvlcpy into tokens delimited by commas and look them up */
	for (cptr = strtok_r(lvlcpy, ",", &saveptr); cptr;
	     cptr = strtok_r(NULL, ",", &saveptr)) {
		if (! (ret = dbg_name_lookup(cptr))) {
			DBG(ERR, "Unknown debug level name: %s.", cptr);
			return 1;
		} else {

			/* OR these together to take care of overlap etc. */
			*conv |= ret;
		}
	}

	return 0;
}

/* dbg_name_lookup
 * Try to find the given name in a list of valid debug names.
 * Return the value of the name if available. Return 0 if name is unknown.
 */
uint64_t dbg_name_lookup(char * name)
{
	if (! strncasecmp(name, "ALL", strlen(name)))
		return 0xFFFFFFFFFFFFFFFFLLU;
	if (! strncasecmp(name, "SEND", strlen(name)))
		return SEND_1 | SEND_2 | SEND_3;
	if (! strncasecmp(name, "RECV", strlen(name)))
		return RECV_1 | RECV_2 | RECV_3;
	if (! strncasecmp(name, "VRFY", strlen(name)))
		return VRFY_1 | VRFY_2 | VRFY_3;
	if (! strncasecmp(name, "MAIN", strlen(name)))
		return MAIN_1 | MAIN_2 | MAIN_3;
	if (! strncasecmp(name, "HOLD", strlen(name)))
		return HOLD_1 | HOLD_2 | HOLD_3;
	if (! strncasecmp(name, "KERN", strlen(name)))
		return KERN_1 | KERN_2;
	if (! strncasecmp(name, "DEV", strlen(name)))
		return DEV_1 | DEV_2 | DEV_INOUT;

	/* Return 0 if name not found */
	return 0;
}

/* set_timers: Verify that any user-requested timers have sane values.
 * If they do, set the global variables UPDATE_TIMER and INVALID_TIMER.
 * Return 1 if there is a problem, otherwise return 0. */
uint32_t set_timers(char * reqUpdateT, char * reqInvalidT, char * reqHolddownT)
{
	int tmpUpdate, tmpInvalid, tmpHolddown;

	if (reqUpdateT) {
		if ((tmpUpdate = atoi(reqUpdateT)) <= 0) {
			DBG(ERR, "Bad update timer.");
			return 1;
		}
	} else {
		tmpUpdate = DEFAULT_UPDATE_TIMER;
	}

	if (reqInvalidT) {
		if ((tmpInvalid = atoi(reqInvalidT)) <= 0) {
			DBG(ERR, "Bad invalid timer.");
			return 1;
		}
	} else {
		tmpInvalid = DEFAULT_INVALID_TIMER;
	}

	if (reqHolddownT) {
		if ((tmpHolddown = atoi(reqHolddownT)) <= 0) {
			DBG(ERR, "Bad holddown timer.");
			return 1;
		}
	} else {
		tmpHolddown = DEFAULT_HOLDDOWN_TIMER;
	}

	if (tmpUpdate > MAX_UPDATE_TIMER) {
		DBG(ERR, "Max update timer is %u sec.", MAX_UPDATE_TIMER);
		return 1;
	}

	if (tmpInvalid > MAX_INVALID_TIMER) {
		DBG(ERR, "Max invalid timer is %u sec.", MAX_INVALID_TIMER);
		return 1;
	}

	/* Update timer must be less than the invalid timer */
	if (tmpUpdate >= tmpInvalid) {
		DBG(ERR, "Update timer must be less than the invalid timer.");
		return 1;
	}

	/* Set the timers */
	DBG(MAIN_1, "Timer settings: Invalid %d, Update %d, Holddown %d.",
	    tmpInvalid, tmpUpdate, tmpHolddown);

	INVALID_TIMER = tmpInvalid;
	UPDATE_TIMER = tmpUpdate;
	HOLDDOWN_TIMER = tmpHolddown;

	return 0;
}

/* request_network
 * Tell ADVRP about a local network that should be advertised.
 * Return 1 if there is a problem, otherwise return 0.
 */
int request_network(char * prefix, updateRecord * reqNet, int * netCount)
{
	int i;
	updateRecord * cur, * prev;
	char * ptr1, * ptr2;
	char * saveptr;
	char ipstr[INET_ADDRSTRLEN];
	in_addr_t network;
	uint8_t preflen;
	uint64_t hash;

	/* Validation */
	ptr1 = strtok_r(prefix, "/", &saveptr);
	strncpy(ipstr, ptr1, sizeof(ipstr));
	ptr2 = strtok_r(NULL, "/", &saveptr);
	if (ptr1) {
		if (check_network(ptr1)) {
			DBG(ERR, "Invalid network ID for -n option.");
			return 1;
		}
	} else {
		DBG(ERR, "No network ID found for -n option.");
		return 1;
	}

	if (ptr2) {
		if (check_preflen(ptr2) < 0) {
			DBG(ERR, "Invalid prefix length for -n option.");
			return 1;
		}
	} else {
		DBG(ERR, "No prefix length argument found for -n option.");
		return 1;
	}

	/* AND the network and subnet mask to make sure this network is on
	 * a valid boundary. If it isn't, just "round down" and use the correct
	 * network.
	 */
	preflen = atoi(ptr2);
	network = inet_addr(ipstr) & make_snmask(preflen);

	/* Network string was valid. Add this as a new requested network and
	 * begin populating the temporary updateRecord structure to hold this
	 * route. Other fields are added later.  */
	reqNet[*netCount].network = network;
	reqNet[*netCount].preflen = preflen;
	reqNet[*netCount].metric = 0;

	(*netCount)++;

	return 0;
}

/* request_interface: Add an interface that is desired -- whether or not an
 * interface exists with this name will be checked later.
 * Return NULL if there is a problem, otherwise return a pointer to the new
 * last interface. */
interface * request_interface(char * optarg, interface ** firstIf)
{
	interface * newIf, * iface, * prev;

	/* Validation */
	if (strlen(optarg) + 1 > IFNAMSIZ) {
		DBG(ERR, "-i argument is too long.");
		return NULL;
	}

	if (! (newIf = malloc(sizeof(interface)))) {
		DBG(ERR, "Malloc failed.");
		return NULL;
	}

	/* Just populate the "ifname" field and maintain the linked list, 
	 * so we know what interfaces were asked for later on. */
	strncpy(newIf->ifname, optarg, sizeof(newIf->ifname));
	newIf->next = NULL;

	/* Add this interface to the end of the list */
	for (iface = *firstIf; iface; iface = iface->next)
		prev = iface;

	if (*firstIf) {
		prev->next = newIf;
	} else {
		*firstIf = newIf;
		newIf->prev = prev;
	}

	return newIf;
}

/* add_local_prefixes
 * Try to add all prefixes that this router should initially know about.
 * Normally, these should be locally connected networks or a default route, but
 * they could be anything.
 * Return 1 on error, otherwise return 0.
 */
int add_local_prefixes(updateRecord * reqNet, int netCount)
{
	int i;
	updateRecord * cur;
	uint64_t hash;
	struct in_addr addr;
	char * ipstr;

	DBG(MAIN_1, "%d requested prefixes to add.", netCount);

	for (i = 0; i < netCount; i++) {
		addr.s_addr = reqNet[i].network;
		ipstr = (char *) inet_ntoa(addr);
		hash = make_hash(reqNet[i].network, reqNet[i].preflen);
		neighbor_route_add(firstNeighbor, 0, &reqNet[i], ipstr,
		                   hash);
	}

	return 0;
}

/* check_network
 * Make sure this is a valid network in dotted decimal notation.
 * Return 1 on error, otherwise return 0.
 */
int check_network(char * netstr)
{
	char * octetp, * savep;
	int valid = 0;                 /* Number of valid octets found */

	octetp = strtok_r(netstr, ".", &savep);

	if (check_octet(octetp))
		return 1;
	else
		valid++;
	
	while (octetp = strtok_r(NULL, ".", &savep))
		if (check_octet(octetp))
			return 1;
		else
			valid++;

	return ! (valid == 4);
}

int check_octet(char * octetp)
{
	int num;

	if ((strlen(octetp) < 4) && is_number(octetp)) {
		num = atoi(octetp);

		if (num > 255 || num < 0) {
			return 1;
		} else {
			return 0;
		}
	} else {
		return 1;
	}
}

/* check_preflen
 * Determine if the given string is a valid prefix length.
 * Return -1 if there is a problem, otherwise return the prefix length as an
 * integer.
 */
int check_preflen(char * s)
{
	int plen;

	if (is_number(s))
		atoi(s);
	else
		return -1;

	plen = atoi(s);

	if (plen > 32 || plen < 0)
		return -1;
	else
		return plen;
}


/* is_number
 * Return 1 if the string s contains all numeric characters, otherwise return 
 * 0. No negative numbers.
 */
int is_number(char * s)
{
	int i, first;

	/* No negative numbers */
	if (s[0] == '-')
		return 0;

	/* Are any chars in the string not a number? */
	for (i = 0; s[i] != '\0'; i++)
		if (! isdigit(s[i]))
			return 0;

	/* If the string length was greater than 0, this was a number */
	return (i > 0);
}

/* make_snmask
 * Convert a prefix length into a subnet mask.
 */
in_addr_t make_snmask(uint8_t preflen)
{
	uint32_t highbit = 1 << 31;  /* Leftmost bit flipped on */

	/* The following won't work with a prefix length of 0, so handle that
	 * now */
	if (preflen == 0)
		return 0;

	/* If the prefix length was greater than the max, reduce it to 32. */
	if (preflen > 32)
		preflen = 32;

	/* Bake a subnet mask using a prefix length in five easy steps:
	 * 1. Zero out MASK, then set MASK's highest order bit equal to one
	 * 2. Right shift MASK by one less than the PREFIX LENGTH
	 * 3. Subtract 1 from MASK; now all the bits we want are flipped to 0
	 * 4. Get the one's complement of MASK (flip all 0s to 1s, vice versa)
	 * 5. MASK is now in network byte order; convert it to host order
	 */
	return ntohl(~((highbit >> preflen - 1) - 1));
}

int pidfile_create(void)
{
	FILE * file;
	struct stat pidstat = { .st_ino = 0 };
	int pid = getpid();
	char pidstr[10];

	if (! pid) {
		DBG(ERR, "Could not get pid.");
		return 1;
	}
	DBG(DEV_2, "ADVRP's pid is %d", pid);
	snprintf(pidstr, sizeof(pidstr), "%d", pid);

	errno = 0;

	/* Check if file exists */
	if (! stat(PIDFILE, &pidstat)) {

		/* If the problem was something other than "file doesn't exist",
		 * print the error message */
		if (errno && errno != ENOENT) {
			DBG(ERR, "Couldn't create pidfile: %s",
			    strerror(errno));
		} else {
			DBG(ERR, "ADVRP seems to already be running "
			    "(does %s exist?)", PIDFILE);
		}
		return 1;
	}

	/* Open the file for writing */
	errno = 0;
	file = fopen(PIDFILE, "w");
	if (errno) {
		DBG(ERR, "Could not open pidfile %s: %s", PIDFILE,
		    strerror(errno));
		return 1;
	}

	/* Write the pid, close the file, and return */
	errno = 0;
	fwrite(pidstr, strlen(pidstr), 1, file);
	if (errno) {
		DBG(ERR, "Could not write pid to %s: %s", PIDFILE,
		    strerror(errno));
		return 1;
	}
	fclose(file);
	return 0;
}

int pidfile_delete(void)
{
	errno = 0;
	unlink(PIDFILE);
	if (errno) {
		DBG(ERR, "Could not remove pidfile %s: %s", PIDFILE,
		    strerror(errno));
		return 1;
	}
	return 0;
}

void usage(void)
{
	DBG(ERR, USAGE_MESSAGE);
	cleanup();
}

void cleanup(void)
{
	interface * cif;
	internalRoute * iroute;
	neighbor * neigh;
	neighborRoute * nroute, * nextnroute;
	hdNetwork * hdn;
	void * next;

	/* TODO: Close the open sockets in recv and send threads */

	pthread_mutex_lock(&mutexAdvrp);

	if (gain_privs()) {
		DBG(ERR, "Could not become root. Can't disable routing or "
		    "remove the pidfile.");
	}

	/* Free all linked lists */
	for (iroute = firstInternalRoute; iroute; iroute = next) {
		/* Remove internal routes from the kernel routing table, too */
		kernel_route(iroute, DEL);
		next = iroute->next;
		free(iroute);
	}

	for (hdn = firstHdNetwork; hdn; hdn = next) {
		next = hdn->next;
		free(hdn);
	}

	for (neigh = firstNeighbor; neigh; neigh = next) {
		next = neigh->next;

		/* free neighbor's routes */
		for (nroute = neigh->firstroute; nroute; nroute = nextnroute) {
			nextnroute = nroute->next;
			free(nroute);
		}
		free(neigh);
	}

	for (cif = firstInterface; cif; cif = next) {
		next = cif->next;
		free(cif);
	}


	/* Disable routing */
	router(OFF);

	/* Remove our pidfile */
	pidfile_delete();

	exit(0);
}
